home *** CD-ROM | disk | FTP | other *** search
/ PC World Komputer 2010 April / PCWorld0410.iso / pluginy Firefox / 722 / 722.xpi / chrome / noscript.jar / content / noscript / DNS.js < prev    next >
Text File  |  2010-02-12  |  10KB  |  353 lines

  1. function DNSRecord(record) {
  2.   this.ts = Date.now();
  3.   var ttl;
  4.   if (record) {
  5.     try {
  6.       this.canonicalName = record.canonicalName;
  7.     } catch(e) {}
  8.     this.entries = [];
  9.     
  10.     try {
  11.       for (;;) this.entries.push(record.getNextAddrAsString());
  12.     } catch(e) {
  13.       // NS_ERROR_NOT_AVAILABLE, no more records
  14.     }
  15.     ttl = this.TTL;
  16.     if (!this.entries.length) this.valid = false;
  17.   } else {
  18.     this.valid = false;
  19.   }
  20.   if (!this.valid) ttl = Thread.canSpin ? this.INVALID_TTL_ASYNC : this.INVALID_TTL_SYNC;
  21.   this.expireTime = this.ts + ttl;
  22. }
  23.  
  24. DNSRecord.prototype = {
  25.   INVALID_TTL_ASYNC: 3000,
  26.   INVALID_TTL_SYNC: 8000,
  27.   TTL: 60000,
  28.   valid: true,
  29.   ts: 0,
  30.   entries: [],
  31.   canonicalName: '',
  32.   expireTime: 0,
  33.   refreshing: false,
  34.   
  35.   isLocal: function(all) {
  36.     return all
  37.       ? "everyLocal" in this
  38.         ? this.everyLocal
  39.         : this.everyLocal = this.entries.every(DNS.isLocalIP, DNS)
  40.       : "someLocal" in this
  41.         ? this.someLocal
  42.         : this.someLocal = this.entries.some(DNS.isLocalIP, DNS)
  43.       ;
  44.   },
  45.   get expired() {
  46.     return Date.now() > this.expireTime;
  47.   }
  48.   
  49. }
  50.  
  51.  
  52. var DNS = {
  53.   
  54.   get logFile() {
  55.     delete this.logFile;
  56.     var logFile = CC["@mozilla.org/file/directory_service;1"]
  57.       .getService(CI.nsIProperties).get("ProfD", CI.nsIFile);
  58.     logFile.append("noscript_dns.log");
  59.     return this.logFile = logFile;
  60.   },
  61.   logEnabled: false,
  62.   log: function(msg) {
  63.     try {
  64.       if (!this.logStream) {
  65.         const logFile = this.logFile;
  66.         const logStream = CC["@mozilla.org/network/file-output-stream;1"]
  67.           .createInstance(CI.nsIFileOutputStream);
  68.         logStream.init(logFile, 0x02 | 0x08 | 0x10, 0600, 0 );
  69.         this.logStream = logStream;
  70.         const header="*** Log start at "+new Date().toGMTString()+"\n";
  71.         this.logStream.write(header,header.length);
  72.       }
  73.       
  74.       if (msg!=null) {
  75.         msg += "\n";
  76.         this.logStream.write(msg,msg.length);
  77.       }
  78.       this.logStream.flush();
  79.     } catch(ex) {
  80.       dump(ex.message+"\noccurred logging this message:\n"+msg);
  81.     }
  82.   },
  83.   
  84.   get _dns() {
  85.     delete this._dns;
  86.     return this._dns = CC["@mozilla.org/network/dns-service;1"]
  87.                   .getService(CI.nsIDNSService);
  88.   },
  89.   
  90.   _cache: {
  91.     CAPACITY: 400, // when we purge, we cut this to half
  92.     _map: {},
  93.     _ext: {},
  94.     
  95.     get count() {
  96.       return this._map.__count__;
  97.     },
  98.     get: function(key) {
  99.       return key in this._map && this._map[key];
  100.     },
  101.     put: function(key, entry) {
  102.       if (!(key in this._map)) {
  103.         if (this.count >= this.CAPACITY) {
  104.           this.purge();
  105.         }
  106.       }
  107.       this._map[key] = entry;
  108.     },
  109.     evict: function(host) {
  110.       return (host in this._map) && (delete this._map[host]);
  111.     },
  112.     
  113.     purge: function() {
  114.       var max = this.CAPACITY / 2;
  115.       if (this.count < max) return;
  116.       var l = [];
  117.       var map = this._map;
  118.       for (var key in map) {
  119.         l.push({ k: key, t: map[key].ts});
  120.       }
  121.       this._doPurge(map, l, max);
  122.     },
  123.     
  124.     _oldLast: function(a, b) {
  125.       return a.t > b.t ? -1 : a.t < b.t ? 1 : 0; 
  126.     },
  127.     
  128.     putExt: function(host) {
  129.       this._ext[host] = true; // Date.now();
  130.       // we prefer to store a few bytes indefinitely rather than fall for DNS rebinding...
  131.       // if (this._ext.__count__ > 800) this._purgeExtCache();
  132.     },
  133.     isExt: function(host) {
  134.       return host in this._ext;
  135.     },
  136.     
  137.     _purgeExtCache: function() {
  138.       var l = [];
  139.       var map = this._extCache;
  140.       for (var key in map) {
  141.         l.push({ k: key, t: map[key]});
  142.       }
  143.       this._doPurge(map, l, l.length / 2);
  144.     },
  145.     
  146.     _doPurge: function(map, l, max) {
  147.       l.sort(this._oldLast);
  148.       for (var j = l.length; j-- > max;) {
  149.         delete map[l[j].k];
  150.       }
  151.     }
  152.   },
  153.   
  154.   get _idn() {
  155.     delete this._idn;
  156.     return this._idn =  CC["@mozilla.org/network/idn-service;1"]
  157.       .getService(CI.nsIIDNService);
  158.   },
  159.   
  160.   _invalidRx: /[^\w\-\.]/,
  161.   checkHostName: function(host) {
  162.     if (this._invalidRx.test(host) && !this.isIP(host)) {
  163.       try {
  164.         host = this._idn.convertUTF8toACE(host);
  165.       } catch(e) {
  166.         return false;
  167.       }
  168.       return !this._invalidRx.test(host);
  169.     }
  170.     return true;
  171.   },
  172.   
  173.   _resolving: {},
  174.   resolve: function(host, flags, callback) { 
  175.     flags = flags || 0;
  176.  
  177.     var elapsed = 0, t;
  178.     var cache = this._cache;
  179.     var async = IOUtil.asyncNetworking && Thread.canSpin || !!callback;
  180.     
  181.     var dnsRecord = cache.get(host);
  182.     if (dnsRecord) {
  183.       // cache invalidation, if needed
  184.       if (dnsRecord.expired && !dnsRecord.refreshing) {
  185.         if (dnsRecord.valid && !(flags & 1)) {
  186.           // refresh async
  187.           dnsRecord.refreshing = true;
  188.           DNS._dns.asyncResolve(host, flags, new DNSListener(function() {
  189.               if (DNS.logEnabled) DNS.log("Async " + host);
  190.               cache.put(host, dnsRecord = new DNSRecord(this.record));
  191.             }), Thread.currentQueue);
  192.         } else {
  193.           flags |= 1;
  194.         }
  195.         if (flags & 1) {  
  196.           dnsRecord = null;
  197.           cache.evict(host);
  198.         }
  199.       }
  200.     }
  201.     if (dnsRecord) {
  202.       if (ABE.consoleDump) ABE.log("Using cached DNS record for " + host);
  203.     } else if (this.checkHostName(host)) {
  204.       
  205.       if (async) {
  206.         var resolving = this._resolving;
  207.   
  208.         if (host in resolving) {
  209.           ABE.log("Already resolving " + host);
  210.           
  211.           if (callback) {
  212.             resolving[host].push(callback);
  213.             return null;
  214.           }
  215.         } else resolving[host] = callback ? [callback] : [];
  216.         
  217.         var ctrl = {
  218.           running: true,
  219.           startTime: Date.now()
  220.         };
  221.         
  222.         var status = Components.results.NS_OK;
  223.         
  224.         
  225.         var resolve = function() {
  226.           DNS._dns.asyncResolve(host, flags, new DNSListener(function() {
  227.             if (DNS.logEnabled) DNS.log("Async " + host);
  228.             cache.put(host, dnsRecord = new DNSRecord(this.record));
  229.             ctrl.running = false;
  230.             var callbacks = resolving[host];
  231.             delete resolving[host];
  232.             if (ABE.consoleDump && t) {
  233.               elapsed = Date.now() - t;
  234.               ABE.log("Async DNS query on " + host + " done, " + elapsed + "ms, callbacks: " + (callbacks && callbacks.length));
  235.             }
  236.             
  237.             if (callbacks && callbacks.length)
  238.               for each(var cb in callbacks)
  239.                 cb(dnsRecord);
  240.             
  241.           }), Thread.currentQueue);
  242.           if (ABE.consoleDump) ABE.log("Waiting for DNS query on " + host);
  243.           if (!callback) Thread.spin(ctrl);
  244.         }
  245.         
  246.         if (callback) {
  247.           t = Date.now();
  248.           resolve();
  249.           return null;
  250.         }
  251.         
  252.         Thread.runWithQueue(resolve);
  253.         
  254.         if (!Components.isSuccessCode(status)) throw status;
  255.         
  256.         elapsed = ctrl.elapsed || 0;
  257.       } else {
  258.         t = Date.now();
  259.         if (ABE.consoleDump) ABE.log("Performing DNS query on " + host);
  260.         if (DNS.logEnabled) DNS.log("Sync " + host);
  261.         cache.put(host, dnsRecord = new DNSRecord(this._dns.resolve(host, flags)));
  262.         elapsed = Date.now() - t;
  263.       }
  264.     } else {
  265.       this._cache.put(host, dnsRecord = new DNSRecord(null)); // invalid host name
  266.     }
  267.     
  268.     if (ABE.consoleDump) ABE.log("DNS query on " + host + " done, " + elapsed + "ms");
  269.     
  270.     if (callback) {
  271.       callback(dnsRecord);
  272.     } else {
  273.       if (!(dnsRecord && dnsRecord.valid)) throw Components.results.NS_ERROR_UNKNOWN_HOST;
  274.     }
  275.     return dnsRecord;
  276.   },
  277.   
  278.   evict: function(host) {
  279.     ABE.log("Removing DNS cache record for " + host);
  280.     return this._cache.evict(host);
  281.   },
  282.   
  283.   invalidate: function(host) {
  284.     var dnsRecord = this._cache.get(host);
  285.     if (!dnsRecord.valid) return false;
  286.     dnsRecord.valid = false;
  287.     dnsRecord.expireTime = 0;
  288.     return true;
  289.   },
  290.   
  291.   getCached: function(host) {
  292.     return this._cache.get(host);
  293.   },
  294.   
  295.   isCached: function(host) {
  296.     var res =  this._cache.get(host);
  297.     return res && (res.valid || !res.expired);
  298.   },
  299.   
  300.   isLocalURI: function(uri, all) {
  301.     var host;
  302.     try {
  303.       host = uri.host;
  304.     } catch(e) {
  305.       return false;
  306.     }
  307.     if (!host) return true; // local file:///
  308.     return this.isLocalHost(host, all);
  309.   },
  310.   
  311.   isLocalHost: function(host, all) {
  312.     if (host == "localhost") return true;
  313.     if (this.isIP(host)) {
  314.       return this.isLocalIP(host);
  315.     }
  316.  
  317.     if (all && this._cache.isExt(host)) return false;
  318.   
  319.     var res = this.resolve(host, 0).isLocal(all);
  320.  
  321.     if (!res) {
  322.       this._cache.putExt(host);
  323.     }
  324.     
  325.     return res;
  326.   },
  327.   
  328.   isLocalIP: function(addr) {
  329.     // see https://bug354493.bugzilla.mozilla.org/attachment.cgi?id=329492 for a more verbose but incomplete (missing IPV6 ULA) implementation
  330.     // Relevant RFCs linked at http://en.wikipedia.org/wiki/Private_network
  331.     return /^(?:(?:0|127|10|169\.254|172\.(?:1[6-9]|2\d|3[0-1])|192\.168)\..*\.[^0]\d*$|(?:(?:255\.){3}255|::1?)$|F(?:[CDF][0-9A-F]|E[89AB])[0-9A-F:]+::)/i.test(addr);
  332.   },
  333.   
  334.   isIP: function(host) {
  335.     return /^(?:\d+\.){3}\d+$|:.*:/.test(host);
  336.   }
  337.   
  338. };
  339.  
  340. function DNSListener(callback) {
  341.   if (callback) this.callback = callback;
  342. };
  343. DNSListener.prototype = {
  344.   QueryInterface: xpcom_generateQI([CI.nsIDNSListener, CI.nsISupports]),
  345.   record: null,
  346.   status: 0,
  347.   callback: null,
  348.   onLookupComplete: function(req, rec, status) {
  349.     this.record = rec;
  350.     this.status = status;
  351.     if (this.callback) this.callback();
  352.   }
  353. };